﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.Office.Interop.Excel;
using PengSW.ExcelHelper;
using PengSW.NotifyPropertyChanged;
using static PengSW.RuntimeLog.RL;

namespace MultiLanguage_AnalyzeProject
{
    class MultiLanguageModel : NotifyPropertyChangedObject
    {
        private const string ConnectionString = "Data Source=localhost\\sqlexpress; Initial Catalog=MultiLanguage; Integrated Security=true;";

        private readonly Tuple<string, Tuple<Regex, string>[]>[] _FilePatterns = new Tuple<string, Tuple<Regex, string>[]>[]
        {
            new Tuple<string, Tuple<Regex, string>[]>(
                "*.xaml",
                new Tuple<Regex, string>[]
                {
                    new Tuple<Regex, string>(new Regex("Title=\"([^{].*?)\""), "Title=\"{{DynamicResource STR{0:000000}}}\""),
                    new Tuple<Regex, string>(new Regex("Content=\"([^{].*?)\""), "Content=\"{{DynamicResource STR{0:000000}}}\""),
                    new Tuple<Regex, string>(new Regex("Text=\"([^{].*?)\""), "Text=\"{{DynamicResource STR{0:000000}}}\""),
                    new Tuple<Regex, string>(new Regex("Header=\"([^{].*?)\""), "Header=\"{{DynamicResource STR{0:000000}}}\""),
                    new Tuple<Regex, string>(new Regex("ToolTip=\"([^{].*?)\""), "ToolTip=\"{{DynamicResource STR{0:000000}}}\""),
                }
            ),
            new Tuple<string, Tuple<Regex, string>[]>(
                "*.cs",
                new Tuple<Regex, string>[]
                {
                    new Tuple<Regex, string>(new Regex("\"(([^\"]|\\\"|\"\")+?)\""), "_T(\"STR{0:000000}\")")
                }
            ),
        };

        private readonly Regex[] ExcludeFileNames = new Regex[]
        {
            new Regex(".+\\.g\\.cs$"),
            new Regex(".+\\.g\\.i\\.cs$"),
            new Regex(".*\\\\AssemblyInfo\\.cs$"),
            new Regex(".*\\.AssemblyAttributes\\.cs$"),
            new Regex(".+\\.Designer\\.cs$"),
        };

        private readonly Regex[] ExcludeTexts = new Regex[]
        {
            new Regex("^Title=\"\\d+\"$"),
            new Regex("^Content=\"\\d+\"$"),
            new Regex("^Text=\"\\d+\"$"),
            new Regex("^Header=\"\\d+\"$"),
            new Regex("^Tooltip=\"\\d+\"$"),
            new Regex("^\"\\d+\"$"),
            new Regex("^Title=\"[^\\w]+\"$"),
            new Regex("^Content=\"[^\\w]+\"$"),
            new Regex("^Text=\"[^\\w]+\"$"),
            new Regex("^Header=\"[^\\w]+\"$"),
            new Regex("^\"[^\\w]+\"$"),
        };

        public int StartId { get => _StartId; set { SetValue(ref _StartId, value, nameof(StartId)); } }
        private int _StartId = 0;

        public void ExportExcel()
        {
            StringCode[] aStringCodes;
            using (var aDataContext = new MultiLanguageDataContext(ConnectionString))
            {
                aStringCodes = (from r in aDataContext.StringCode where r.Id > StartId && r.Type == 0 select r).ToArray();
            }

            ApplicationClass aExcelApp = new ApplicationClass
            {
                Visible = true
            };
            Workbook aWorkbook = aExcelApp.Workbooks.Add();
            Worksheet aWorksheet = aWorkbook.Worksheets[1] as Worksheet;
            aWorksheet.SetCellValue(1, 1, "Id");
            aWorksheet.SetCellValue(1, 2, "上下文");
            aWorksheet.SetCellValue(1, 3, "中文");
            aWorksheet.SetCellValue(1, 4, "英文");
            int i = 2;
            foreach (var aStringCode in aStringCodes)
            {
                aWorksheet.SetCellValue(i, 1, aStringCode.Id);
                aWorksheet.SetCellValue(i, 2, aStringCode.Content);
                aWorksheet.SetCellValue(i, 3, aStringCode.Zh);
                aWorksheet.SetCellValue(i, 4, aStringCode.En);
                i++;
            }
        }

        public void ImportExcel(string aFileName)
        {
            List<Tuple<int, string, string>> aItems = new List<Tuple<int, string, string>>();
            ApplicationClass aExcelApp = new ApplicationClass
            {
                Visible = true
            };
            Workbook aWorkbook = aExcelApp.Workbooks.Add(aFileName);
            Worksheet aWorksheet = aWorkbook.Worksheets[1] as Worksheet;
            for (int i = 2; !string.IsNullOrWhiteSpace(aWorksheet.GetCellValue(i, 1)); i++)
            {
                if (!int.TryParse(aWorksheet.GetCellValue(i, 1), out int aId)) continue;
                string aZh = aWorksheet.GetCellValue(i, 3);
                string aEn = aWorksheet.GetCellValue(i, 4);
                aItems.Add(new Tuple<int, string, string>(aId, aZh, aEn));
            }
            aWorkbook.Close();
            aExcelApp.Quit();

            using (MultiLanguageDataContext aDataContext = new MultiLanguageDataContext(ConnectionString))
            {
                foreach (var aItem in aItems)
                {
                    L($"Update {aItem.Item1}: {aItem.Item2}");
                    var aStringCode = aDataContext.StringCode.FirstOrDefault(r => r.Id == aItem.Item1);
                    if (aStringCode == null) continue;
                    aStringCode.Zh = aItem.Item2;
                    aStringCode.En = aItem.Item3;
                    aStringCode.IsReplacable = true;
                }
                aDataContext.SubmitChanges();
            }
        }

        public void CreateXaml(string aFolder)
        {
            StringCode[] aStringCodes;
            using (var aDataContext = new MultiLanguageDataContext(ConnectionString))
            {
                aStringCodes = (from r in aDataContext.StringCode where r.IsReplacable select r).ToArray();
            }
            
            StringBuilder zh_cn = new StringBuilder();
            StringBuilder en_us = new StringBuilder();
            
            zh_cn.AppendLine("<ResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" xmlns:s=\"clr-namespace:System;assembly=mscorlib\">");
            en_us.AppendLine("<ResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" xmlns:s=\"clr-namespace:System;assembly=mscorlib\">");

            zh_cn.AppendLine($"    <s:String x:Key=\"STR000000\">语言</s:String>");
            en_us.AppendLine($"    <s:String x:Key=\"STR000000\">Language</s:String>");

            foreach (var aStringCode in aStringCodes)
            {
                zh_cn.AppendLine($"    <s:String x:Key=\"STR{aStringCode.Id:000000}\">{aStringCode.Zh}</s:String>");
                en_us.AppendLine($"    <s:String x:Key=\"STR{aStringCode.Id:000000}\">{aStringCode.En}</s:String>");
            }

            zh_cn.AppendLine("</ResourceDictionary>");
            en_us.AppendLine("</ResourceDictionary>");
            
            File.WriteAllText(Path.Combine(aFolder, "zh-cn.xaml"), zh_cn.ToString(), Encoding.UTF8);
            File.WriteAllText(Path.Combine(aFolder, "en-us.xaml"), en_us.ToString(), Encoding.UTF8);
        }

        public void Upgrade(string aFolder)
        {
            StringCode[] aAllStringCodes;
            using (var aDataContext = new MultiLanguageDataContext(ConnectionString))
            {
                aAllStringCodes = (from r in aDataContext.StringCode where r.IsReplacable && r.Id > StartId select r).ToArray();
            }

            foreach (var aFilePattern in _FilePatterns)
            {
                var aFileNames = Directory.GetFiles(aFolder, aFilePattern.Item1, SearchOption.AllDirectories);
                foreach (var aFileName in aFileNames)
                {
                    var aStringCodes = (from r in aAllStringCodes where r.SourceFile == aFileName select r).ToArray();
                    if (aStringCodes.Length == 0) continue;
                    L(aFileName);
                    var aTexts = File.ReadAllLines(aFileName, Encoding);
                    for (int i = 0; i < aTexts.Length; i++)
                    {
                        var aText = aTexts[i];
                        var aStringBuilder = new StringBuilder(aText);
                        foreach (var aStringCode in (from r in aStringCodes where r.Content == aText select r))
                        {
                            aStringBuilder.Replace(aStringCode.Text, string.Format(aStringCode.Replace, aStringCode.Id));
                        }
                        aTexts[i] = aStringBuilder.ToString();
                    }
                    File.WriteAllLines(aFileName, aTexts, Encoding);
                }
            }
        }

        public Encoding Encoding { get => _Encoding; set { SetValue(ref _Encoding, value, nameof(Encoding)); } }
        private Encoding _Encoding = Encoding.UTF8;

        public Encoding[] Encodings => _Encodings;
        private static readonly Encoding[] _Encodings = new Encoding[] { Encoding.UTF8, Encoding.Unicode, Encoding.UTF32, Encoding.UTF7, Encoding.ASCII, Encoding.BigEndianUnicode, Encoding.Default };

        public void Analyze(string aFolder)
        {
            List<StringCode> aStringCodes = new List<StringCode>();
            foreach (var aFilePattern in _FilePatterns)
            {
                var aFileNames = Directory.GetFiles(aFolder, aFilePattern.Item1, SearchOption.AllDirectories);
                foreach (var aFileName in aFileNames)
                {
                    if ((from r in ExcludeFileNames where r.IsMatch(aFileName) select r).FirstOrDefault() != null) continue;

                    L(aFileName);
                    var aLines = File.ReadAllLines(aFileName, Encoding);
                    int aLineCount = 1;
                    foreach (var aLine in aLines)
                    {
                        foreach (var aRegex in aFilePattern.Item2)
                        {
                            var aMatches = aRegex.Item1.Matches(aLine);
                            foreach (Match aMatch in aMatches)
                            {
                                if ((from r in ExcludeTexts where r.IsMatch(aMatch.Value) select r).FirstOrDefault() != null) continue;
                                var aText = aMatch.Value;
                                var aZh = aMatch.Groups[1].Value;
                                L($"Line {aLineCount}: {aText}");
                                aStringCodes.Add(new StringCode { SourceFile = aFileName, LineNo = aLineCount, Content = aLine, Text = aText, Zh = aZh, En = aZh, Replace = aRegex.Item2, Type = 0 });
                            }
                        }
                        aLineCount++;
                    }
                }
            }

            using (MultiLanguageDataContext aDataContext = new MultiLanguageDataContext(ConnectionString))
            {
                if (!aDataContext.DatabaseExists()) aDataContext.CreateDatabase();
                aDataContext.StringCode.InsertAllOnSubmit(aStringCodes);
                aDataContext.SubmitChanges();
            }
        }
    }
}
